#include <stdlib.h>
#include <stdio.h>
#include "libringqueue.h"

struct ring_queue *ring_queue_init(uint32_t max_node_sum)
{
	struct ring_queue *mrq = (struct ring_queue *)malloc(sizeof(struct ring_queue));
	mrq->max_node_sum = max_node_sum;
	mrq->cur_node_sum = 0;
	INIT_LIST_HEAD(&mrq->ring_queue_list);
	pthread_mutex_init(&mrq->mutex, NULL);
	return mrq;
}

void ring_queue_deinit(struct ring_queue *mrq)
{
	struct list_head *pos;
	struct ring_queue_node *rqn;
	list_for_each(pos, &mrq->ring_queue_list)
	{
		rqn = list_entry(pos, struct ring_queue_node, list);
		free(rqn->data);
		free(rqn);
	}
	pthread_mutex_destroy(&mrq->mutex);
	free(mrq);
}
/**
 * @brief Get the ring queue node object
 *
 * @param mrq
 * @param tail_flag : tail_flag==0,means get first node , tail_flag==1,means get last node
 * @return ** struct ring_queue_node*
 */
struct ring_queue_node *get_ring_queue_node(struct ring_queue *mrq, uint8_t tail_flag)
{
	struct ring_queue_node *mrqn_pos;
	if (tail_flag != 0)
		mrqn_pos = list_last_entry(&mrq->ring_queue_list, struct ring_queue_node, list);
	else
		mrqn_pos = list_entry(mrq->ring_queue_list.next, struct ring_queue_node, list);
	return mrqn_pos;
}

/**
 * @brief
 *
 * @param mrq
 * @param data
 * @param size
 * @param tail_flag : tail_flag==0,means adding node in head 3,2,1,0 ,tail_flag==1,means adding node in tail  0,1,2,3
 * @param overwrite_flag : when queue was full ,then overwrite data
 * @param overwrite_tail : when queue was full ,then overwrite data,in head or tail
 * @return ** int : 0 ->success , RET_QUEUE_FULL
 */

int32_t ring_queue_write(struct ring_queue *mrq, void *data, uint32_t size, uint8_t tail_flag, uint8_t overwrite_flag,uint8_t overwrite_tail)
{
	struct ring_queue_node *mrqn = (struct ring_queue_node *)malloc(sizeof(struct ring_queue_node));
	struct ring_queue_node *mrqn_pos;
	mrqn->data = data;
	mrqn->node_size = size;

	pthread_mutex_lock(&mrq->mutex);
	if (mrq->cur_node_sum > mrq->max_node_sum -1)
	{
		if (overwrite_flag == 0)
		{
			printf("ring_queue was full , not write node into queue\n");
			free(mrqn);
			pthread_mutex_unlock(&mrq->mutex);
			return RET_QUEUE_FULL;
		}
		else
		{
			mrqn_pos = get_ring_queue_node(mrq, overwrite_tail);
			list_replace(&mrqn_pos->list, &mrqn->list);
			free(mrqn_pos->data);
			free(mrqn_pos);
		}
	}
	else
	{
		if (tail_flag != 0)
			list_add_tail(&mrqn->list, &mrq->ring_queue_list);
		else
			list_add(&mrqn->list, &mrq->ring_queue_list);
		mrq->cur_node_sum++;
	}
	pthread_mutex_unlock(&mrq->mutex);
	return 0;
}

/**
 * @brief  user must free data themselves
 *
 * @param mrq
 * @param data
 * @param size
 * @param tail_flag : read data at head or tail
 * @return ** uint32_t : 0 ->success , RET_QUEUE_EMPTY
 */
int32_t ring_queue_read(struct ring_queue *mrq, void **data, uint32_t *size, uint8_t tail_flag)
{
	struct ring_queue_node *mrqn_pos;
	pthread_mutex_lock(&mrq->mutex);
	mrqn_pos = get_ring_queue_node(mrq, tail_flag);
	if (list_empty(&mrq->ring_queue_list))
	{
		printf("ring_queue was empty , read failed\n");
		pthread_mutex_unlock(&mrq->mutex);
		return RET_QUEUE_EMPTY;
	}
	*data = mrqn_pos->data;
	*size = mrqn_pos->node_size;
	list_del(&mrqn_pos->list);
	free(mrqn_pos);
	mrq->cur_node_sum--;
	pthread_mutex_unlock(&mrq->mutex);
	return 0;
}

/*
	user must free data themselves
*/
int32_t ring_queue_delete(struct ring_queue *mrq, void **data, uint8_t tail_flag)
{
	struct ring_queue_node *mrqn_pos;
	pthread_mutex_lock(&mrq->mutex);
	mrqn_pos = get_ring_queue_node(mrq, tail_flag);
	if (list_empty(&mrq->ring_queue_list))
	{
		pthread_mutex_unlock(&mrq->mutex);
		return RET_QUEUE_EMPTY;
	}
	list_del(&mrqn_pos->list);
	*data = mrqn_pos->data;
	free(mrqn_pos);
	mrq->cur_node_sum--;
	pthread_mutex_unlock(&mrq->mutex);
	return 0;
}

void list_each_print(struct ring_queue *mrq)
{
	struct list_head *pos;
	struct ring_queue_node *mrqn_pos;
	printf("----------------------------------------------\n");
	list_for_each(pos, &mrq->ring_queue_list)
	{
		mrqn_pos = list_entry(pos, struct ring_queue_node, list);
		printf("%s\n", (uint8_t *)mrqn_pos->data);
	}
	printf("----------------------------------------------\n");
}

